package com.mingqijia.gassafety.shared.log;

import com.alibaba.fastjson.JSONObject;
import com.gap.basic.base.CommonResponse;
import com.gap.basic.exception.BizException;
import com.mingqijia.gassafety.shared.constant.Constants;
import com.mingqijia.gassafety.shared.user.UserBackend;
import com.mingqijia.gassafety.shared.utils.ApplicationUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;

/**
 * 日志切面。有性能消耗，如果系统对性能要求较高，可使用try-catch-finally形式记录日志
 *
 * @author Damon S.
 * @version v0.2
 * @date 2021-05-30 17:05
 */
@Aspect
@Slf4j
@Component
public class LogAspect {
    public static final Integer REST_TIMEOUT_INTERVAL = 5000;

    @Pointcut("execution(* com.mingqijia.gassafety.*.web.facade.impl.*Impl.*(..))")
    public void pointcut1() { }

//    @Pointcut("execution(* com.mingqijia.gassafety.*.controller.*Controller.*(..))")
//    public void pointcut2() { }

    @Around("pointcut1()")
    public Object around1(ProceedingJoinPoint joinPoint) throws Throwable {
        return logAspectAdvice(joinPoint);
    }

//    @Around("pointcut2()")
//    public Object around2(ProceedingJoinPoint joinPoint) throws Throwable {
//        return logAspectAdvice(joinPoint);
//    }
    @Around("execution(* com.mingqijia.gassafety.*.controller.*Controller.*(..))) || " +
            "execution(* com.mingqijia.gassafety.*.controller.*.*Controller.*(..))")
    public Object around(ProceedingJoinPoint point) {
        Object returnValue = null;
        Long watchTime = 0L;
        try {
            StopWatch watch = new StopWatch();
            Object[] args = point.getArgs();
            String currentFunc = point.getSignature().getDeclaringTypeName() + "." + point.getSignature().getName();
            log.info("{} 入参: {}",currentFunc,args);
            watch.start();
            returnValue = point.proceed(args);
            watch.stop();
            watchTime = watch.getTime();
            String logMsg = "";
            if (returnValue != null) {
                logMsg = currentFunc + " 出参: " + JSONObject.toJSONString(returnValue)
                        + " execute time:" + watchTime + "ms";
            }else {
                logMsg = currentFunc + " 出参: 为空" + " execute time:" + watchTime + "ms";
            }
            if (watchTime > REST_TIMEOUT_INTERVAL) {
                log.info("#WARNING TIME_OUT - " + logMsg);
            } else {
                log.info(logMsg);
            }
        }catch (BizException e) {
            log.info("#ERROR AspectjLog记录日志异常" + e.getMessage() + " execute time:" + watchTime + "ms", e);
            returnValue = CommonResponse.failure(e.getMessage());
        }catch (Exception e) {
            log.info("#ERROR AspectjLog记录日志异常" + e.getMessage() + " execute time:" + watchTime + "ms", e);
            throw new RuntimeException(e.getMessage());
        } catch (Throwable e) {
            log.info("#ERROR AspectjLog记录日志异常" + e.getMessage() + " execute time:" + watchTime + "ms", e);
            throw new RuntimeException(e.getMessage());
        }
        return returnValue;
    }

    public Object logAspectAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        Signature sgn = joinPoint.getSignature();
        String aspectMethod = sgn.getDeclaringTypeName() + Constants.STR_DOT + sgn.getName();
        String headerValues = ApplicationUtils.getRequestHeaders();
        Object resultObject = Constants.STR_EMPTY;

        long startAt = System.currentTimeMillis();
        try {
            // 执行正常逻辑并返回
            resultObject = joinPoint.proceed();
        } finally {
            LogObject logObject = LogObject.builder().others(headerValues)
                    .duration(System.currentTimeMillis() - startAt).response(resultObject)
                    .traceId(MDC.get(Constants.LOG_TRACE_ID)).request(joinPoint.getArgs())
                    .eventName(aspectMethod).userId(getUserBackend().getUserId()).build();
            log.info("Restful接口调用日志：接口调用全程信息={}", logObject.toString());
        }
        return resultObject;
    }

    private UserBackend getUserBackend() {
        UserBackend backend = UserBackend.DEFAULT;
        try {
            // 获取用户
            backend = ApplicationUtils.getWorkingUser();
        } catch (Exception e) {
            log.warn("Restful接口调用日志：用户未登录或请求接口不需登录验证[请忽略此日志]");
        }
        return backend;
    }
}
